
import moment from 'moment'
import XEUtils from 'xe-utils'
// 切割url
function splitUrl(url) {
  return url.split('?')[0]
}

function toArrayTree(list, options) {
  return XEUtils.toArrayTree(list, options)
}

/**
 * 日期时间格式化
 * @param {date} time js的date类型或时间戳
 * @param {string} format 自定义时间格式，选填，默认为'{y}-{m}-{d} {h}:{i}:{s}'
 * @return {string} 默认格式 2018-09-01 10:55:00
 */
function formatDate(time, format) {
  time = time || new Date()
  format = format || '{y}-{m}-{d}'
  let date = time
  if (typeof time !== 'object') {
    if (('' + time).length === 10) time = +time * 1000
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay()
  }
  const timeStr = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
    let value = formatObj[key]
    if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
    if (result.length > 0 && value < 10) {
      value = '0' + value
    }
    return value || 0
  })
  return timeStr
}

/**
 * 日期格式转时间戳
 * @param {date} date date类型 2019-05-24 14:22:17
 * @return {string} 1558678937000
 */
function getTimestamp(date) {
  if (!date) {
    return new Date().getTime()
  }
  if (typeof date === 'string') {
    date = date.replace(/-/g, '/')
  }
  return new Date(date).getTime()
}

/**
 * 日期格式转换成和当前时间相比剩余天数
 * @param {date} startDate 开始时间点 startDate类型 2020-08-26 10:48:36，该参数不存在时，开始时间点为当前时间
 * @param {date} endDate 结束时间点 endDate类型 2020-08-26 10:57:36
 * @return {string} number 数字n
 */
function getRemainDay(endDate, startDate) {
  if (typeof endDate === 'string') {
    // "-" 移动端ios部分机型不识别，需要转化
    endDate = endDate.replace(/-/g, '/')
  }
  if (startDate && typeof startDate === 'string') {
    // "-" 移动端ios部分机型不识别，需要转化
    startDate = startDate.replace(/-/g, '/')
  }
  const curr = startDate ? new Date(startDate).getTime() : new Date().getTime()
  const end = new Date(endDate).getTime()
  const cha = Math.ceil((end - curr) / 1000 / 3600 / 24)
  return cha
}

/**
 * 获取7天前的 00:00:00 至当天的前一天 23:59:59
 * 例如: 2020-10-14 11:01:36 ==> ["2020-10-07 00:00:00","2020-10-13 23:59:59"]
 * @return {array}  ["2020-10-07 00:00:00","2020-10-13 23:59:59"]
 */
function getWeekAgo() {
  const currTs = new Date(new Date().toLocaleDateString()).getTime()
  const startTime = formatDate(currTs - 24 * 60 * 60 * 7 * 1000)
  const endTime = formatDate(currTs - 1000)
  return [startTime, endTime]
}

/**
 *获取当前时间的字符串形式
 *例如2020-10-14 ==》 20201014
 * @param {date} date date类型 2019-05-24 14:22:17
 * @return {string}
 */
function dateToString(date) {
  const year = date.getFullYear()
  let month = (date.getMonth() + 1).toString()
  let day = (date.getDate()).toString()
  if (month.length === 1) {
    month = '0' + month
  }
  if (day.length === 1) {
    day = '0' + day
  }
  const dateTime = `${year}${month}${day}`
  return dateTime
}

/**
 * 最近多少天
 * @param {number} days 默认30天
 * @param {string} format 自定义时间类型，默认 '{y}-{m}-{d}', '{y}-{m}-{d} {h}:{i}:{s}'
 * @return {array} ['2020-10-01','2020-10-14']
 */
function getLatelyDays(days, startDate, format) {
  days = days || 30
  format = format || '{y}-{m}-{d}'
  // let currTs = new Date(new Date().toLocaleDateString()).getTime()
  const currTs = getTimestamp(formatDate(new Date(), format))
  const startTime = startDate || formatDate(currTs, format)
  const endTime = formatDate(currTs + 24 * 60 * 60 * days * 1000, format)
  return [startTime, endTime]
}

/**
 * 日期时间文字化
 * 比较传入时间与当前本地时间，文字化显示日期时间
 * @param {date} time js的date类型或时间戳
 * @param {string} cFormat 自定义两天前的时间格式，选填
 * @return {string} 刚刚 n分钟前 n小时前 1天前
 */
function txtFormatTime(time, format) {
  if (!time) {
    return 'null'
  }
  const d = new Date(time)
  const now = Date.now()

  const diff = (now - d) / 1000

  if (diff < 30) {
    return '刚刚'
  } else if (diff < 3600) {
    // less 1 hour
    return Math.ceil(diff / 60) + '分钟前'
  } else if (diff < 3600 * 24) {
    return Math.ceil(diff / 3600) + '小时前'
  } else if (diff < 3600 * 24 * 2) {
    return '1天前'
  } else {
    return formatDate(time, format)
  }
}

/**
     * 得到开始和结束日期，得到中间所有天返回数组
     * @param {String} string 开始日期'2021-7-1'
     * @param {String} String 结束日期'2021-8-1'
     * @return {Array} ['2021-07-01', '2021-07-01'...., '2021-08-01']
     */
function getDayArr(startDay, endDay) {
  let startVal = moment(new Date(startDay)).format('YYYY-MM-DD')
  const dayArr = []
  while (moment(startVal).isBefore(endDay)) {
    dayArr.push(startVal)
    // 自增
    startVal = moment(new Date(startVal)).add(1, 'day').format('YYYY-MM-DD')
  }
  // 将结束日期的天放进数组
  dayArr.push(moment(new Date(endDay)).format('YYYY-MM-DD'))
  return dayArr
}

/**
 * 对象参数序列化
 * @param {object} obj 对象参数
 * @return {string} a=1&b=2&c=3
 */
function objToUrlParams(obj) {
  let str = ''
  Object.keys(obj).forEach(v => {
    if (obj[v] !== undefined) {
      str += `${encodeURIComponent(v)}=${encodeURIComponent(obj[v])}&`
    }
  })
  return str.slice(0, -1)
}

/**
 * 获取地址参数
 * @param {string} url 指定地址，默认取当前页地址
 * @return {string} { a: 1, b: 2, c: 3 }
 */
function getQueryObject(url) {
  url = url || window.location.href
  const search = url.substring(url.lastIndexOf('?') + 1)
  const obj = {}
  const reg = /([^?&=]+)=([^?&=]*)/g
  search.replace(reg, (rs, $1, $2) => {
    const name = decodeURIComponent($1)
    let val = decodeURIComponent($2)
    val = String(val)
    obj[name] = val
    return rs
  })
  return obj
}

/**
 * 创建唯一的字符串
 * @return {string} ojgdvbvaua40
 */
function createUniqueString() {
  const timestamp = +new Date() + ''
  const randomNum = parseInt((1 + Math.random()) * 65536) + ''
  return (+(randomNum + timestamp)).toString(32)
}

/**
 * 文件下载
 * @param {string} url 文件下载链接url，带上接口参数
 * @param {string} name 前端自定义的下载文件名，带上文件后缀名，选填
 * chrome会优先使用接口返回的命名，edge会优先使用此name
 */
function downloadFile(url, name) {
  url = url || ''
  name = name || ''
  const ele = document.createElement('a')
  ele.target = '_blank'
  ele.href = url
  ele.download = name
  document.body.appendChild(ele)
  ele.click()
  document.body.removeChild(ele)
}

/**
 * 获取字符串的字节长度
 * @param {String} str 字符串
 * @returns {number} 字节长度
 */
function getByteLength(str) {
  let len = 0
  for (let i = 0; i < str.length; i++) {
    // eslint-disable-next-line
    if (str[i].match(/[^\x00-\xff]/gi) != null) {
      len += 2
    } else {
      len += 1
    }
  }
  return len
}

/**
 * 数字千分化
 * @param {number} num 数字
 * @return {number} 10,000
 */
function toThousands(num) {
  return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
}

/**
 * 数字存储大小格式化
 * @param {number} num 存储大小 单位：Byte
 * @param {number} digits 保留几位小数
 * @return {string} 2MB
 */
function toStorage(num, digits) {
  digits = digits || 2
  if (num < 1024) {
    return num + 'B'
  }
  num = (num * 1000) / 1024
  const si = [
    { value: 1e18, symbol: 'E' },
    { value: 1e15, symbol: 'P' },
    { value: 1e12, symbol: 'T' },
    { value: 1e9, symbol: 'G' },
    { value: 1e6, symbol: 'M' },
    { value: 1e3, symbol: 'K' }
  ]
  for (let i = 0; i < si.length; i++) {
    if (num >= si[i].value) {
      return (
        (num / (si[i].value * 1.024)).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol + 'B'
      )
    }
  }
}

/**
 * 函数防抖
 * @param {function} fn 函数
 * @param {number} t 等待时间（毫秒）
 * @return {function}
 */
function debounce(fn, t = 500) {
  return XEUtils.debounce(fn, t, {
    leading: true,
    trailing: false
  })
}

/**
 * 函数节流
 * @param {function} fn 函数
 * @param {number} t 间隔时间（毫秒）
 * @return {function}
 */
function throttle(fn, t) {
  let timeId
  let firstTime = true
  const interval = t || 500
  return function() {
    const args = arguments
    if (firstTime) {
      fn.apply(this, args)
      firstTime = false
      return
    }
    if (timeId) {
      return
    }
    timeId = setTimeout(() => {
      clearTimeout(timeId)
      timeId = null
      fn.apply(this, args)
    }, interval)
  }
}

/**
 * 深克隆
 * @param {object|array} 源数据
 * @return {object|array}
 */
function deepClone(source) {
  return XEUtils.clone(source, true)
}

/**
 * 遍历树的方法
 * @tree {array} 源数据
 */
function filterTree(tree, fn) {
  return XEUtils.filterTree(tree, fn)
}

/**
 * 获取数据类型
 * @param {any} data 数据
 * @return {string} 'array'
 */
function getDataType(data) {
  const str = Object.prototype.toString.call(data)
  return str.match(/\s(\w*)\]/)[1].toLowerCase()
}

/**
 * 对象合并 (对象属性深度合并，其他属性覆盖更新(a覆盖b))
 * @param {object} a 对象
 * @param {object} b 对象
 * @return {object} 合并后的对象 (新)
 */
function objectMerge(a, b) {
  const obj = JSON.parse(JSON.stringify(a))
  return (function merge(target, source) {
    if (typeof target !== 'object') {
      target = {}
    }
    if (Array.isArray(source)) {
      return source.slice()
    }
    Object.keys(source).forEach(property => {
      const sourceProperty = source[property]
      if (typeof sourceProperty === 'object') {
        target[property] = merge(target[property], sourceProperty)
      } else {
        target[property] = sourceProperty
      }
    })
    return target
  })(obj, b)
}

/**
 * 获取日期范围内的所有日期项
 * @param {string} type 'day'按天 'month'按月
 * @param {array} range 日期范围，第一项为开始日期，第二项为结束日期（可选，不传时默认30天或者12个月）
 * @return {array} 该范围内的所有日期项数组
 */
function getDateRangeItems(type, range) {
  const arr = []
  let start, end, times
  if (type === 'day') {
    // 按天
    if (!range || range.length === 0) {
      start = new Date()
      end = new Date()
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 29)
    } else {
      start = new Date(range[0])
      end = new Date(range[1])
    }
    times = (end.getFullYear() - start.getFullYear() + 1) * 366
    for (let i = 0; i < times; i++) {
      arr.push(formatDate(start, '{y}-{m}-{d}'))
      if (formatDate(start, '{y}-{m}-{d}') === formatDate(end, '{y}-{m}-{d}')) {
        break
      }
      start.setTime(start.getTime() + 3600 * 1000 * 24)
    }
  } else if (type === 'month') {
    // 按月
    if (!range || range.length === 0) {
      start = new Date()
      end = new Date()
      start.setMonth(start.getMonth() - 11)
    } else {
      start = new Date(range[0])
      end = new Date(range[1])
    }
    times = (end.getFullYear() - start.getFullYear() + 1) * 12
    for (let i = 0; i < times; i++) {
      arr.push(formatDate(start, '{y}-{m}'))
      if (formatDate(start, '{y}-{m}') === formatDate(end, '{y}-{m}')) {
        break
      }
      start.setMonth(start.getMonth() + 1)
    }
  }
  return arr
}

/**
 * 对象数组转换成带有children属性的对象数组
 * @param {array} list 数据源
 * @param {string} pAttr 参照属性名
 * @param {string} cAttr 比较属性名
 * @param {number | string} 参照属性名对应的属性值
 * @return {array}
 */
export function treeData(list, pAttr, cAttr, pAttrVal) {
  const pList = []
  const cList = []
  list.map(val => {
    if (val[cAttr] !== pAttrVal) {
      cList.push(val)
    } else {
      pList.push({ children: [], ...val })
    }
  })
  pList.map((val, index) => {
    pList[index].children = cList.filter(c => c[cAttr] === val[pAttr])
  })
  return pList
}

/**
 * 文件转base64
 * @param {object} file 数据源
 * @return {array}
 */
export function getBase64(file) {
  return new Promise(function(resolve, reject) {
    const reader = new FileReader()
    let imgResult = ''
    reader.readAsDataURL(file)
    reader.onload = function() {
      imgResult = reader.result
    }
    reader.onerror = (error) => {
      reject(error)
    }
    reader.onloadend = () => {
      resolve(imgResult)
    }
  })
}

/**
 * 通过字节动态计算input长度
 * @param {String} str 数据源输入文本
 * @param {Number} length 想要限制文本的长度
 * @return { calcLength, flag, newStr } calcLength计算的长度 flag触发超出长度的标识 newStr新生成的文本
 */
export function getLength(str, length) {
  let calcLength = 0
  let flag = ''
  const content = str
  let newStr = ''
  for (let i = 0; i < content.length; i++) {
    if (content[i].charCodeAt() > 0 && content[i].charCodeAt() < 255) {
      calcLength += 1
    } else {
      calcLength += 2
    }
    if (calcLength <= length) {
      newStr += content[i].charAt()
    }
  }
  flag = calcLength > length
  return { calcLength, flag, newStr }
}

/**
 * 字符串转bytes
 * @param {String} str 数据源输入文本
 * @return {Array} bytes字节流数组
 */
export function stringToByte(str) {
  const blockSize = 16
  const bytes = []
  let c
  const len = str.length
  for (let i = 0; i < len; i++) {
    c = str.charCodeAt(i)
    if (c >= 0x010000 && c <= 0x10FFFF) {
      bytes.push(((c >> 18) & 0x07) | 0xF0)
      bytes.push(((c >> 12) & 0x3F) | 0x80)
      bytes.push(((c >> 6) & 0x3F) | 0x80)
      bytes.push((c & 0x3F) | 0x80)
    } else if (c >= 0x000800 && c <= 0x00FFFF) {
      bytes.push(((c >> 12) & 0x0F) | 0xE0)
      bytes.push(((c >> 6) & 0x3F) | 0x80)
      bytes.push((c & 0x3F) | 0x80)
    } else if (c >= 0x000080 && c <= 0x0007FF) {
      bytes.push(((c >> 6) & 0x1F) | 0xC0)
      bytes.push((c & 0x3F) | 0x80)
    } else {
      bytes.push(c & 0xFF)
    }
  }
  if (bytes.length % blockSize !== 0) {
    bytes.length += blockSize - bytes.length % blockSize
  }
  return bytes
}

/**
 * 获取当前日期
 * @return {Array} 日期和星期数的集合
 */
export const getNowFormatDate = _ => {
  const date = new Date()
  const year = date.getFullYear()
  let month = date.getMonth() + 1
  let nowDate = date.getDate()
  const day = date.getDay()
  const h = date.getHours()
  const m = date.getMinutes() < 10 ? ('0' + date.getMinutes()) : date.getMinutes()
  month = month < 10 ? '0' + month : month
  nowDate = nowDate < 10 ? '0' + nowDate : nowDate
  const formatDate = `${year}年${month}月${nowDate}日`
  const week = '星期' + '日一二三四五六'.charAt(day)
  const time = h + ':' + m
  return {formatDate, week, time}
}


/**
 * byte转字符串
 * @param {Array} arr 数据源输入文本
 * @return {String} 字符串
 */
export function byteToString(arr) {
  if (typeof arr === 'string') {
    return arr
  }
  let str = ''
  const _arr = arr
  for (let i = 0; i < _arr.length; i++) {
    if (_arr[i]) {
      const one = _arr[i].toString(2)
      const v = one.match(/^1+?(?=0)/)
      if (v && one.length === 8) {
        const bytesLength = v[0].length
        let store = _arr[i].toString(2).slice(7 - bytesLength)
        for (let st = 1; st < bytesLength; st++) {
          store += _arr[st + i].toString(2).slice(2)
        }
        str += String.fromCharCode(parseInt(store, 2))
        i += bytesLength - 1
      } else {
        str += String.fromCharCode(_arr[i])
      }
    }
  }
  return str
}

export default {
  // 处理url
  splitUrl,
  // 数组转树结构
  toArrayTree,
  // 日期时间格式化
  formatDate,
  // 日期格式转时间戳
  getTimestamp,
  // 日期格式转化成剩余时间n天
  getRemainDay,
  // 日期时间文字化
  txtFormatTime,
  // 获取时间字符串形式
  dateToString,
  // 获取间隔时间
  getDayArr,
  // 对象参数序列化
  objToUrlParams,
  // 获取地址参数
  getQueryObject,
  // 创建唯一的字符串
  createUniqueString,
  // 文件下载
  downloadFile,
  // 获取字符串的字节长度
  getByteLength,
  // 数字千分化
  toThousands,
  // 数字存储大小格式化
  toStorage,
  // 函数防抖
  debounce,
  // 函数节流
  throttle,
  // 深克隆
  deepClone,
  // 获取数据类型
  getDataType,
  // 对象合并
  objectMerge,
  // 获取日期范围内的所有日期项
  getDateRangeItems,
  // 获取一周前零点至今天的前一天23:59:59
  getWeekAgo,
  // 获取最近n天
  getLatelyDays,
  //
  treeData,
  // 文件转为base64
  getBase64,
  // 通过字节动态计算input长度
  getLength,
  // 字符串转byte
  stringToByte,
  // byte转字符串
  byteToString,
  // 树结构数据循环
  filterTree
}

